﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using Galaxy.ModuleInterface;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

namespace Galaxy.Loader.Information
{
    /// <summary>Список объектов</summary>
    [Serializable]
    [XmlType("Объекты")]
    public class CObjectList
    {
        /// <summary>Список объектов</summary>
        private List<CObjectInformation> f_ObjectsList = new List<CObjectInformation>();

        /// <summary>Список объектов</summary>
        [XmlArray("Объект")]
        public List<CObjectInformation> ModulesList
        {
            get { return f_ObjectsList; }
            set { f_ObjectsList = value; }
        }

        /// <summary>Инициализация нового списка объектов</summary>
        public CObjectList() { }

        /// <summary>Инициализация нового списка модулей Галактики</summary>
        /// <param name="Object">Объекты для сохранения</param>
        public CObjectList(params object[] Object)
        {
            Array.ForEach<object>(Object, delegate(object O)
            {
                f_ObjectsList.Add(new CObjectInformation(O));
            });
        }

        /// <summary>Создать все объекты из списка</summary>
        /// <returns>Массив объектов из списка описаний</returns>
        public object[] CreateAll()
        {
            return f_ObjectsList.ConvertAll<object>(delegate(CObjectInformation ObjectInformation)
            {
                return ObjectInformation.CreateInstance();
            }).ToArray();
        }

        /// <summary>Сериализация списка в поток в формате XML</summary>
        /// <param name="DataStream">Поток данных для сериализации</param>
        public void SerializeXML(Stream DataStream)
        {
            new XmlSerializer(this.GetType()).Serialize(DataStream, this);
        }

        /// <summary>Сериализация списка в бинарный поток</summary>
        /// <param name="DataStream">Поток данных</param>
        public void SerializeBinary(Stream DataStream)
        {
            new BinaryFormatter().Serialize(DataStream, this);
        }

        /// <summary>Сериализаня списка в XML файл</summary>
        /// <param name="FileName">Имя XML файла</param>
        public void SerializeXML(string FileName)
        {
            using(FileStream File = new FileStream(FileName, FileMode.OpenOrCreate,
                 FileAccess.Write, FileShare.Write))
                SerializeXML(File);
        }

        /// <summary>Сериализация списка в бинарный файл</summary>
        /// <param name="FileName">Имя бинарного файла</param>
        public void SerializeBinary(string FileName)
        {
            using(FileStream File = new FileStream(FileName, FileMode.OpenOrCreate,
                 FileAccess.Write, FileShare.Write))
                SerializeBinary(File);
        }

        /// <summary>Весериализация списка из XML потока</summary>
        /// <param name="DataStream">Поток данных XML</param>
        /// <returns>Список объектов</returns>
        public static CObjectList DeserializeXML(Stream DataStream)
        {
            return new XmlSerializer(typeof(CObjectList)).Deserialize(DataStream) as CObjectList;
        }

        /// <summary>Десериализация списка из бинарного потока</summary>
        /// <param name="DataStream">Бинарный поток</param>
        /// <returns>Список объектов</returns>
        public static CObjectList DeserializeBinary(Stream DataStream)
        {
            return new BinaryFormatter().Deserialize(DataStream) as CObjectList;
        }

        /// <summary>ДЕсериализация объектов из XML файла</summary>
        /// <param name="FileName">Имя XML файла</param>
        /// <returns>Список объектов из файла</returns>
        public static CObjectList DeserializeXML(string FileName)
        {
            using(FileStream File = new FileStream(FileName,
                 FileMode.Open, FileAccess.Read, FileShare.Read))
                return DeserializeXML(File);
        }

        /// <summary>Десериализация списка объектов из бинарного файла</summary>
        /// <param name="FileName">Имя бинарного файла со списком объектов</param>
        /// <returns>Список объектов</returns>
        public static CObjectList DeserializeBinary(string FileName)
        {
            using(FileStream File = new FileStream(FileName,
                 FileMode.Open, FileAccess.Read, FileShare.Read))
                return DeserializeBinary(File);
        }

        public void Add(object Object)
        {
            CObjectInformation ObjectInformation = new CObjectInformation(Object);
            if(!f_ObjectsList.Contains(ObjectInformation))
                f_ObjectsList.Add(ObjectInformation);
        }

        public void Add(IEnumerable Objects)
        {
            foreach(object Object in Objects)
            {
                CObjectInformation ObjectInfoemation = new CObjectInformation(Object);
                if(!f_ObjectsList.Contains(ObjectInfoemation))
                    f_ObjectsList.Add(ObjectInfoemation);
            }
        }

        public bool Contains(object Object)
        {
            return f_ObjectsList.Contains(new CObjectInformation(Object));
        }
    }

    /// <summary>Структура данных о объекте</summary>
    [Serializable]
    [XmlType("Объект")]
    public class CObjectInformation
    {
        /// <summary>Поле файла сборки</summary>
        private string f_FileName;
        /// <summary>Поле имени класса объекта</summary>
        private string f_ClassName;

        /// <summary>Сведенья о файле сборки</summary>
        [XmlElement("Сборка")]
        public string FileName { get { return f_FileName; } set { f_FileName = value; } }

        /// <summary>Имя класса модуля из сборки</summary>
        [XmlElement("Класс")]
        public string ClassName { get { return f_ClassName; } set { f_ClassName = value; } }

        /// <summary>Инициализация новой структуры данных о объекте</summary>
        public CObjectInformation() { }

        /// <summary>Инициализация новой структуры данных о объекте</summary>
        /// <param name="File">Файл сборки</param>
        /// <param name="ClassName">Имя класса сборки</param>
        public CObjectInformation(string File, string ClassName)
        {
            f_FileName = File;
            f_ClassName = ClassName;
        }

        /// <summary>Инициализация новой структуры данных о объекте</summary>
        /// <param name="Object">Объект для сохранения</param>
        public CObjectInformation(object Object)
        {
            Type ModuleType = Object.GetType();
            f_FileName = ModuleType.Assembly.Location;
            f_ClassName = ModuleType.FullName;
        }

        /// <summary>Инициализация новой структуры данных о объекте</summary>
        ///<param name="StrData">Строковый эквивалент блока данных о объекте</param>
        public CObjectInformation(string StrData)
        {
            f_ClassName = StrData.Substring(StrData.LastIndexOf('|') + 1);
            f_FileName = StrData.Remove(StrData.LastIndexOf('|'));
        }

        /// <summary>Создать объект</summary>
        /// <returns>Объект класса</returns>
        public object CreateInstance()
        {
            Assembly ModuleAssembly = Assembly.LoadFile(f_FileName);

            Type ModuleType = ModuleAssembly.GetType(f_ClassName);
            return Activator.CreateInstance(ModuleType);
        }

        /// <summary>Сериализация информации о объекте в поток данных в формате XML</summary>
        /// <param name="stream">Поток данных</param>
        public void SerializeXML(Stream stream)
        {
            new XmlSerializer(this.GetType()).Serialize(stream, this);
        }

        /// <summary>Сериализация информации о объекте в поток данных в двоичном формате</summary>
        /// <param name="stream">Поток данных</param>
        public void SerializeBinary(Stream stream)
        {
            new BinaryFormatter().Serialize(stream, this);
        }

        /// <summary>Десериализация данных о объекте из потока в XML формате</summary>
        /// <param name="stream">Поток данных</param>
        /// <returns>Данные о объекте</returns>
        public static CObjectInformation DeserializeXML(Stream stream)
        {
            return (new XmlSerializer(typeof(CObjectInformation)).Deserialize(stream)) as CObjectInformation;
        }

        /// <summary>Десериализация данных о объекте из потока в двоичном формате</summary>
        /// <param name="stream">Поток данных</param>
        /// <returns>Данные о объекте</returns>
        public static CObjectInformation DeserializeBinary(Stream stream)
        {
            return (new BinaryFormatter().Deserialize(stream)) as CObjectInformation;
        }

        /// <summary>Преобразование в строку</summary>
        /// <returns>Строковый эквивалент</returns>
        public override string ToString()
        {
            return f_FileName + "|" + f_ClassName;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if(obj.GetType() == this.GetType())
                return obj.GetHashCode() == this.GetHashCode();
            else
                return false;
        }
    }
}
